﻿/*
    This file is part of nDoctor Light

    NDoctor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    NDoctor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with NDoctor.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Probel.Ldoc.ViewModels
{
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Input;

    using Probel.Ldoc.Services.Services;
    using Probel.Mvvm.DataBinding;

    public class BaseViewModel : ObservableObject
    {
        #region Fields

        protected readonly IErrorService ErrorService;

        #endregion Fields

        #region Constructors

        public BaseViewModel(IErrorService errorService, bool isAsync)
        {
            this.ErrorService = errorService;
            this.IsAsync = isAsync;
        }

        #endregion Constructors

        #region Properties

        public bool IsAsync
        {
            get;
            set;
        }

        #endregion Properties

        #region Methods

        protected void Async<T>(Func<T> businessProcess, Action<T> guiProcess)
        {
            if (this.IsAsync)
            {
                this.SetWaitCursor();

                var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
                var task = Task.Run<T>(() => businessProcess());
                task.ContinueWith(t =>
                {
                    using (WaitingCursor.While) { guiProcess(t.Result); }
                }, CancellationToken.None, TaskContinuationOptions.NotOnFaulted, scheduler);
                task.ContinueWith(t =>
                {
                    this.ErrorService.HandleError(t.Exception);
                    Mouse.OverrideCursor = Cursors.Arrow;
                }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, scheduler);
            }
            else
            {
                var result = businessProcess();
                guiProcess(result);
            }
        }

        protected void Async(Action businessProcess, Action guiProcess)
        {
            if (this.IsAsync)
            {
                this.SetWaitCursor();

                var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
                var task = Task.Run(() => businessProcess());
                task.ContinueWith(t =>
                {
                    using (WaitingCursor.While) { guiProcess(); }
                }, CancellationToken.None, TaskContinuationOptions.NotOnFaulted, scheduler);
                task.ContinueWith(t =>
                {
                    this.ErrorService.HandleError(t.Exception);
                    Mouse.OverrideCursor = Cursors.Arrow;
                }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, scheduler);
            }
            else
            {
                businessProcess();
                guiProcess();
            }
        }

        private void ResetCursor()
        {
            Mouse.OverrideCursor = null;
        }

        private void SetWaitCursor()
        {
            Mouse.OverrideCursor = Cursors.Wait;
        }

        #endregion Methods

        #region Nested Types

        /// <summary>
        /// During the life time of this object, the mouse is showing the sandglass.
        /// As soon as this object is disposed, the mouse arrow is back to indicate
        /// the work is done.
        /// </summary>
        protected class WaitingCursor : IDisposable
        {
            #region Constructors

            private WaitingCursor()
            {
                Mouse.OverrideCursor = Cursors.Wait;
            }

            #endregion Constructors

            #region Properties

            public static WaitingCursor While
            {
                get { return new WaitingCursor(); }
            }

            #endregion Properties

            #region Methods

            public void Dispose()
            {
                Mouse.OverrideCursor = null;
            }

            #endregion Methods
        }

        #endregion Nested Types
    }
}